/*
 * gen.c
 *
 * Implements a code generation backend that emits C code.
 */



#include "gen_backend_asm.h"
#include "error.h"
#include "gen.h"
#include "mem.h"
#include "symtab.h"
extern char progName [256];

void gen_backend_asm_deinit ()
    {
    /* adding exit call */
	fprintf(gen_fd, "%s:\n", OUT_YOU_GO);
	fprintf(gen_fd, "li %s %d\n", MIPS_V0, MIPS_EXIT_CALL);
	fprintf(gen_fd, "%s\n", MIPS_SYS_CALL);
    }

void gen_backend_asm_init ()
    {
    char line [256];
    char temp [256];

	fprintf(gen_fd, "main:\n");

    /* getting main class from usrdef table*/
    struct usrdef_t * mainCl=  usrdef_lookup (progName);

    /* set gp  to the sp */
    memset(line, 0 , sizeof(line));
    sprintf (line, "%s %s, %s, %d", CMD_ADD_I, MIPS_GP, MIPS_SP, 0);
	fprintf(gen_fd, "%s\n", line);

    /* set the fp */
    memset(line, 0 , sizeof(line));
    sprintf (line, "%s %s, %s, %d", CMD_ADD_I, MIPS_FP , MIPS_GP, (int)mainCl->size);
	fprintf(gen_fd, "%s\n", line);


    /* set the sp */
    memset(line, 0 , sizeof(line));
    sprintf (line, "%s %s, %s, %d", CMD_ADD_I, MIPS_SP , MIPS_FP, 
             symtab_get_func_size(progName));
	fprintf(gen_fd, "%s\n", line);

    /* calculate main func parent address */
    sprintf(temp, "%d(%s)", MIPS_FUNC_PA_CLASS_OFFSET, MIPS_FP);

    /* set the func parent as class */
    memset(line, 0 , sizeof(line));
    sprintf (line, "%s %s, %s", CMD_STORE_WORD, MIPS_GP, temp);
	fprintf(gen_fd, "%s\n", line);

	fprintf(gen_fd, "j %s_%s\n", tolower(progName), tolower(progName));
    
    }

void gen_backend_asm_node(struct mc_t *node) 
{
	if (node == NULL) return;
	int type = node->opcode;
	char line[100];
	strcpy(line, convert_type(type));
    if (strlen (line) >= 1)        
    	strcat(line, " ");

	char *op1 = NULL;
	char *op2 = NULL;
	char *op3 = NULL;
	if (type == MIPS_ADD || type == MIPS_AND || type == MIPS_OR || type == MIPS_SET_LESS_THAN || type == MIPS_XOR || type == MIPS_SUBTRACT) {
		op1 = node->rd;	
		op2 = node->rs;
		op3 = node->rt;	
	} else if (type == MIPS_ADD_I || type == MIPS_AND_I || type == MIPS_OR_I || type == MIPS_SET_LESS_THAN_I || type == MIPS_SHIFT_LEFT || type == MIPS_SHIFT_RIGHT) {
		op1 = node->rd;
		op2 = node->rs;
		op3 = inttostring(node->imm);
	} else if (type == MIPS_BEQ || type == MIPS_BNE) {
		op1 = node->rs;
		op2 = node->rt;
		op3 = node->label;
	} else if (type == MIPS_DIV || type == MIPS_MULT) {
		op1 = node->rs;
		op2 = node->rt;
	} else if (type == MIPS_JUMP) {
		op1 = tolower(node->label);
	} else if (type == MIPS_JUMP_LINK) {
        op1 = tolower(node->label);
    } else if (type == MIPS_LABEL) {
        op1 = tolower(node->label);
    }else if (type == MIPS_LOAD_WORD) {
		op1 = node->rd;
        char offset[100];
		strcpy(offset, inttostring(node->imm));
		strcat(offset, "(");
		strcat(offset, node->rs); /* rd = ram destination */
		strcat(offset, ")");

		op2 = offset;
	} else if (type == MIPS_LOAD_IMM) {
		op1 = node->rd;
		op2 = inttostring(node->imm);
	} else if (type == MIPS_STORE_WORD) {
		op1 = node->rs;
		char offset[100];
		strcpy(offset, inttostring(node->imm));
		strcat(offset, "(");
		strcat(offset, node->rd); /* rd = ram destination */
		strcat(offset, ")");
		op2 = offset;
	} else if (type == MIPS_MFLO) {
		op1 = node->rd;
	} else if (type == MIPS_LOAD_ADD) {
		op1 = node->rd;
		op2 = node->rs;
	}
    if (op1 != NULL)
        {
        strcat(line, op1);
        if (op2 != NULL) {
            strcat(line, ",");
            strcat(line, op2);
            if (op3 != NULL) {
                strcat(line, ",");
                strcat(line, op3);
            }
        }
        }
	fprintf(gen_fd, "%s\n", line);
}

void gen_backend_asm(struct lir_t *lir)
{
    struct mc_t * node;

	while (lir != NULL) {
        node = lir->instruction;
        while (node != NULL)
            {            
		    gen_backend_asm_node(node);
            node = node->next;
            }
		lir = lir->next;
	}

}



/* Returns the size of a primitive type for this backend */
long gen_backend_asm_primitive_size(char *primitive_type)
{
	if (strcmp(primitive_type, PRIMITIVE_TYPE_NAME_INTEGER) == 0)
		return BACKEND_ASM_PRIMITIVE_TYPE_SIZE_INTEGER;
	else if (strcmp(primitive_type, PRIMITIVE_TYPE_NAME_BOOLEAN) == 0)
		return BACKEND_ASM_PRIMITIVE_TYPE_SIZE_BOOLEAN;
	else if (strcmp(primitive_type, PRIMITIVE_TYPE_NAME_POINTER) == 0)
		return BACKEND_ASM_PRIMITIVE_TYPE_SIZE_POINTER;
	else
		return GEN_BACKEND_PRIMITIVE_TYPE_SIZE_UNKNOWN;
}

char *convert_type(int type) {
	if (type == MIPS_ADD) return "add";
	else if (type == MIPS_ADD_I) return "addi";
	else if (type == MIPS_AND) return "and";
	else if (type == MIPS_AND_I) return "andi";
	else if (type == MIPS_BEQ) return "beq";
	else if (type == MIPS_BNE) return "bne";
	else if (type == MIPS_DIV) return "div";
	else if (type == MIPS_JUMP) return "j";
	else if (type == MIPS_LOAD_IMM) return "li";
	else if (type == MIPS_LABEL) return "";
	else if (type == MIPS_JUMP_LINK) return "jal";
	else if (type == MIPS_LOAD_WORD) return "lw";
	else if (type == MIPS_LOAD_ADD) return "la";
	else if (type == MIPS_MFLO) return "mflo";
	else if (type == MIPS_MULT) return "mult";
	else if (type == MIPS_OR) return "or";
	else if (type == MIPS_OR_I) return "ori";
	else if (type == MIPS_XOR) return "xor";
	else if (type == MIPS_SET_LESS_THAN) return "slt";
	else if (type == MIPS_SET_LESS_THAN_I) return "slti";
	else if (type == MIPS_SHIFT_LEFT) return "sll";
	else if (type == MIPS_SHIFT_RIGHT) return "srl";
	else if (type == MIPS_STORE_WORD) return "sw";
	else if (type == MIPS_SUBTRACT) return "sub";
	return NULL;
}
